(function($, undefined){
    
    var AnimOperator = {
        opposites  : {
            'left' : 'right',
            'right': 'left',
            'up'   : 'down',
            'down' : 'up'
        },
        
        defaultData  : {
            from     : {},
            to       : {},
            duration : 250,
            delay    : 0,
            easing   : 'ease-in-out',
            autoClear: true,
            out      : true,
            direction: null,
            reverse  : false,
            running  : {},
            
            before   : $.noop,
            after    : $.noop
        },
    
        animConfigs  : {},
        
        getElAnimData: function($el){
            var data = $el.data('hq-anim');
            if(!data) {
                data = $.extend(true, {}, this.defaultData);
                $el.data('hq-anim', data);
            }
            return data;
        },
        
        initConfig : function($el, data, runConfig) {
            var config = $.extend({}, data, runConfig);
            if (config.reverse && this.opposites[config.direction]) {
                config.direction = this.opposites[config.direction];
            }
            if (data.before) {
                data.before.call(config, $el, data);
            }
            if (runConfig.before) {
                runConfig.before.call(runConfig.scope || $el, $el, data);
            }
            return config;
        },
        
        onTransitionEnd : function(event) {
            var $el       = event.data[0];
            var o         = event.data[1];
            var el        = $el[0];
            var style     = el.style;
            var data      = o.data;
            var runConfig = o.runConfig;
            var property;
            if (data.running[o.name] === undefined) {
                return;
            }
            if (data.autoClear) {
                for (property in data.to) {
                    if (!data.to.hasOwnProperty(property)) {
                        continue;
                    }
                    style[property] = '';
                }
            }
            style.webkitTransitionDuration       = null;
            style.webkitTransitionProperty       = null;
            style.webkitTransitionTimingFunction = null;
            if (data.is3d) {
                $el.parent().css({
                    '-webkit-perspective'    : '',
                    '-webkit-transform-style': ''
                });
            }
            if (data.after) {//内部的after
                data.after.call(runConfig, $el, data);
            }
            if (runConfig.after) {   //外部的after
                runConfig.after.call(runConfig.scope || $el, $el, data);
            }
            $el.unbind('webkitTransitionEnd', AnimOperator.onTransitionEnd);
            delete data.running[o.name];
            event = $el = o = el = style = data = property = null;
        },
        
        getZIndex    : function($el){
            var curZ = $el[0].style.zIndex;
            curZ     = parseInt( curZ == 'auto' ? 0 : curZ );
            curZ     = isNaN(curZ) ? 0 : curZ;
            $el      = null;
            return curZ;
        },
        
        animFnGetter: function(name, cfgObj){
            return function(runConfig){
                cfgObj = $.extend({}, AnimOperator.defaultData, cfgObj);
                cfgObj.from = {};
                cfgObj.to   = {};
                this.data('hq-anim', cfgObj);
                this.anim(name, runConfig);
            };
        },
        
        addAnim: function(name, cfgObj){
            this.animConfigs[name] = cfgObj;
            $.fn[name]    = this.animFnGetter(name, cfgObj);
            name = cfgObj = null;
        }
    };
    
    $.fn.extend({
        
        'anim': function(name, runConfig){
            runConfig     = runConfig || {};
            var $el       = this;
            var data      = AnimOperator.getElAnimData($el);
            var el        = $el[0];
            var style     = el.style;
            var property;
            
            if(data.handler){
                clearTimeout(data.handler);
                data.handler = null;
            }
            if (data.running[name]) {
                AnimOperator.onTransitionEnd({
                    data  : [$el,{
                        data     : data,
                        runConfig: runConfig,
                        name     : name
                    }]
                });
            }
            
            runConfig     = AnimOperator.initConfig($el, data, runConfig);
            this.unbind('webkitTransitionEnd', AnimOperator.onTransitionEnd);
            style.webkitTransitionDuration = '0ms';
            for (property in data.from) {
                if (!data.from.hasOwnProperty(property)) {
                    continue;
                }
                style[property] = data.from[property];
            }
            
            data.handler = setTimeout(function() {
                if (!$el.length) {
                    return;
                }
                
                if (runConfig.is3d === true) {
                    $el.parent().css({
                        '-webkit-perspective'    : '1200',
                        '-webkit-transform-style': 'preserve-3d'
                    });
                }
                style.webkitTransitionDuration       = runConfig.duration + 'ms';
                style.webkitTransitionProperty       = 'all';
                style.webkitTransitionTimingFunction = runConfig.easing;

                $el.one('webkitTransitionEnd', [$el, {
                    data     : data,
                    runConfig: runConfig,
                    name     : name
                }], AnimOperator.onTransitionEnd);
                for (property in data.to) {
                    if (!data.to.hasOwnProperty(property)) {
                        continue;
                    }
                    style[property] = data.to[property];
                }
                
            }, runConfig.delay || 5);
            
            data.running[name]   = data;
            return $el;
        }
        
    });
    
    var defaultAnims = {
        'fade'    : {
            before: function($el, data) {
                var fromOpacity = 1;
                var toOpacity   = 1;
                var curZ        = AnimOperator.getZIndex($el);
                var zIndex      = curZ;
                if (this.out) {
                    toOpacity   = 0;
                } else {
                    zIndex      = curZ + 1;
                    fromOpacity = 0;
                }
                data.from    = {
                    'opacity': fromOpacity,
                    'z-index': zIndex
                };
                data.to      = {
                    'opacity': toOpacity,
                    'z-index': zIndex
                };
            }
        },
        
        'slide'   : {
            direction: 'left',
            cover    : false,
            reveal   : false,
    
            before : function($el, data) {
                var curZ   = AnimOperator.getZIndex($el);
                var zIndex = curZ + 1;
                var toX    = 0;
                var toY    = 0;
                var fromX  = 0;
                var fromY  = 0;
                var elH    = $el.height();
                var elW    = $el.width();
    
                if (this.direction == 'left' || this.direction == 'right') {
                    if (this.out == true) {
                        toX   = -elW;
                    } else {
                        fromX = elW;
                    }
                } else if (this.direction == 'up' || this.direction == 'down') {
                    if (this.out == true) {
                        toY = -elH;
                    } else {
                        fromY = elH;
                    }
                }
    
                if (this.direction == 'right' || this.direction == 'down') {
                    toY   *= -1;
                    toX   *= -1;
                    fromY *= -1;
                    fromX *= -1;
                }
    
                if (this.cover && this.out) {
                    toX    = 0;
                    toY    = 0;
                    zIndex = curZ;
                }
                else if (this.reveal && !this.out) {
                    fromX  = 0;
                    fromY  = 0;
                    zIndex = curZ;
                }
    
                data.from = {
                    '-webkit-transform': 'translate3d(' + fromX + 'px, ' + fromY + 'px, 0)',
                    'z-index': zIndex,
                    'opacity': 0.99
                };
                data.to = {
                    '-webkit-transform': 'translate3d(' + toX + 'px, ' + toY + 'px, 0)',
                    'z-index': zIndex,
                    'opacity': 1
                };
            }
        },
        
        'pop'     : {
            scaleOnExit: true,
            before     : function($el, data) {
                var fromScale   = 1;
                var toScale     = 1;
                var fromOpacity = 1;
                var toOpacity   = 1;
                var curZ        = AnimOperator.getZIndex($el);
                var fromZ       = curZ;
                var toZ         = curZ;
    
                if (!this.out) {
                    fromScale   = 0.01;
                    fromZ       = curZ + 1;
                    toZ         = curZ + 1;
                    fromOpacity = 0;
                } else {
                    if (this.scaleOnExit) {
                        toScale   = 0.01;
                        toOpacity = 0;
                    } else {
                        toOpacity = 0.8;
                    }
                }
    
                data.from = {
                    '-webkit-transform': 'scale(' + fromScale + ')',
                    '-webkit-transform-origin': '50% 50%',
                    'opacity': fromOpacity,
                    'z-index': fromZ
                };
    
                data.to = {
                    '-webkit-transform': 'scale(' + toScale + ')',
                    '-webkit-transform-origin': '50% 50%',
                    'opacity': toOpacity,
                    'z-index': toZ
                };
            }
        },
        
        'flip'    : {
            is3d     : true,
            direction: 'left',
            before   : function($el, data) {
                var rotateProp = 'Y',
                    fromScale  = 1,
                    toScale    = 1,
                    fromRotate = 0,
                    toRotate   = 0;
    
                if (this.out) {
                    toRotate = -180;
                    toScale  = 0.8;
                }
                else {
                    fromRotate = 180;
                    fromScale  = 0.8;
                }
    
                if (this.direction == 'up' || this.direction == 'down') {
                    rotateProp = 'X';
                }
    
                if (this.direction == 'right' || this.direction == 'left') {
                    toRotate   *= -1;
                    fromRotate *= -1;
                }
    
                data.from = {
                    '-webkit-transform': 'rotate' + rotateProp + '(' + fromRotate + 'deg) scale(' + fromScale + ')',
                    '-webkit-backface-visibility': 'hidden'
                };
                data.to = {
                    '-webkit-transform': 'rotate' + rotateProp + '(' + toRotate + 'deg) scale(' + toScale + ')',
                    '-webkit-backface-visibility': 'hidden'
                };
            }
        },
        
        'cube'    : {
            is3d     : true,
            direction: 'left',
            style    : 'outer',
            duration : 250,
            before   : function($el, data) {
                var origin = '0% 0%',
                    fromRotate = 0,
                    toRotate = 0,
                    rotateProp = 'Y',
                    fromZ = 0,
                    toZ = 0,
                    fromOpacity = 1,
                    toOpacity = 1,
                    zDepth,
                    elW = $el.width(),
                    elH = $el.height(),
                    showTranslateZ = true,
                    fromTranslate = ' translateX(0)',
                    toTranslate = '';
    
                if (this.direction == 'left' || this.direction == 'right') {
                    if (this.out) {
                        origin = '100% 100%';
                        toZ = elW;
                        toOpacity = 0.5;
                        toRotate = -90;
                    } else {
                        origin = '0% 0%';
                        fromZ = elW;
                        fromOpacity = 0.5;
                        fromRotate = 90;
                    }
                } else if (this.direction == 'up' || this.direction == 'down') {
                    rotateProp = 'X';
                    if (this.out) {
                        origin = '100% 100%';
                        toZ = elH;
                        toRotate = 90;
                    } else {
                        origin = '0% 0%';
                        fromZ = elH;
                        fromRotate = -90;
                    }
                }
    
                if (this.direction == 'down' || this.direction == 'right') {
                    fromRotate *= -1;
                    toRotate *= -1;
                    origin = (origin == '0% 0%') ? '100% 100%': '0% 0%';
                }
    
                if (this.style == 'inner') {
                    fromZ *= -1;
                    toZ *= -1;
                    fromRotate *= -1;
                    toRotate *= -1;
    
                    if (!this.out) {
                        toTranslate = ' translateX(0px)';
                        origin = '0% 50%';
                    } else {
                        toTranslate = fromTranslate;
                        origin = '100% 50%';
                    }
                }
    
                data.from = {
                    '-webkit-transform': 'rotate' + rotateProp + '(' + fromRotate + 'deg)' + (showTranslateZ ? ' translateZ(' + fromZ + 'px)': '') + fromTranslate,
                    '-webkit-transform-origin': origin
                };
                data.to = {
                    '-webkit-transform': 'rotate' + rotateProp + '(' + toRotate + 'deg) translateZ(' + toZ + 'px)' + toTranslate,
                    '-webkit-transform-origin': origin
                };
            }
        },
        
        'wipe'    : {
            duration: 500,
            out   : false,
            before: function($el, data) {
                var curZ     = AnimOperator.getZIndex($el),
                    mask     = '',
                    toSize   = '100%',
                    fromSize = '100%',
                    w, h;
    
                if (!this.out) {
                    zIndex = curZ + 1;
                    mask   = '-webkit-gradient(linear, left bottom, right bottom, from(transparent), to(#000), color-stop(66%, #000), color-stop(33%, transparent))';
                    w      = $el.width();
                    h      = $el.height();
                    toSize = h * 100 + 'px';
                    fromSize = h;
    
                    this.from = {
                        '-webkit-mask-image': mask,
                        '-webkit-mask-size' : w * 3 + 'px ' + h + 'px',
                        'z-index'           : zIndex,
                        '-webkit-mask-position-x': 0
                    };
                    this.to = {
                        '-webkit-mask-image': mask,
                        '-webkit-mask-size' : w * 3 + 'px ' + h + 'px',
                        'z-index'           : zIndex,
                        '-webkit-mask-position-x': -w * 2 + 'px'
                    };
                }
            }
        }
    };
    
    for(var name in defaultAnims){
        AnimOperator.addAnim(name, defaultAnims[name]);
    }
    
})( jQuery ); 